﻿using System;
using System.IO;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Web;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Xml.Linq;
using Web7.Extensions;

namespace Web7.ExceptionHandling
{
	/// <summary>
	/// 用于管理所有<see cref="IExceptionPublisher"/>的容器
	/// </summary>
	public static class ExceptionPublisherManager
	{
		/// <summary>
		/// 获取已实现（提供）的所有<see cref="IExceptionPublisher"/>实例
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public static ReadOnlyCollection<IExceptionPublisher> AllPublishers
		{
			get
			{
				// TODO: 注意 DI 容器多实例的取法
				IDependencyResolver dependencyResolver = DependencyResolver.Current;
				registeredPublishers = dependencyResolver.GetServices<IExceptionPublisher>().ToList();

				registeredPublishers.Add(dependencyResolver.GetService<IExceptionPublisher>());

				return registeredPublishers.AsReadOnly();
			}
		}
		/// <summary>
		/// 向系统注册一个异常发布程序
		/// </summary>
		public static void RegisterPublisher(IExceptionPublisher publisher)
		{
			RegisterPublisher(publisher, null);
		}
		/// <summary>
		/// 向系统注册一个异常发布程序
		/// </summary>
		public static void RegisterPublisher(IExceptionPublisher publisher, NameValueCollection parameters)
		{
			if (null == publisher)
			{
				return;
			}

			publisher.Initialize(parameters);
			registeredPublishers.Add(publisher);
		}

		static ExceptionPublisherManager()
		{
			return;

			lock (_syncLocker)
			{
				// 防止多线程中的多次执行
				if (initlized)
				{
					return;
				}
				initlized = true;

				var configedPublishers = LoadConfiguration();
				configedPublishers.ForEach(p =>
				{
					var typeName = p["type"];
					p.Remove("type");
					try
					{
						var assembly = Assembly.Load(typeName.Split(',')[1]);
						var type = assembly.GetType(typeName, false);
						if (null != type)
						{
							var publisher = Activator.CreateInstance(type) as IExceptionPublisher;

							if (null != publisher)
							{
								publisher.Initialize(p);
								registeredPublishers.Add(publisher);
							}
						}
					}
					catch (Exception ex)
					{
						throw ex;

						// 忽略错误的加载
						// NOTE:需要日志？可考虑  但在初始化过程中得忽略了
					}
				});
			}
		}

		static List<IExceptionPublisher> registeredPublishers = new List<IExceptionPublisher>();
		static readonly object _syncLocker = new object();
		static bool initlized = false;

		/// <summary>
		/// 从配置中加载异常发布程序
		/// <remarks>
		/// 配置文件内置为 ~/config/ExceptionPublishers.config
		/// </remarks>
		/// </summary>
		/// <returns></returns>
		static IList<NameValueCollection> LoadConfiguration()
		{
			var configedList = new List<NameValueCollection>();

			try
			{
				var basePath = AppDomain.CurrentDomain.BaseDirectory;
				if (!basePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
				{
					basePath += Path.DirectorySeparatorChar;
				}

				// NOTE: 要配置 ExceptionHandling，则对应配置文件必须置于应用程序根目录下的 Configs/ExceptionPublishers.config 文件中。
				var web7ConfigFilePath = Path.Combine(basePath, "Configs/ExceptionPublishers.config".Replace('/', Path.DirectorySeparatorChar));
				if (File.Exists(web7ConfigFilePath))
				{
					var xdoc = XElement.Load(web7ConfigFilePath);

					configedList = xdoc.Elements("publisher")
						.Where(element =>
						{
							var typeAttribute = element.Attribute("type");
							return typeAttribute != null;
						}).Select(publisher =>
						{
							var config = new NameValueCollection();
							var attributes = publisher.Attributes();
							foreach (var attr in attributes)
							{
								config.Add(attr.Name.LocalName, attr.Value);
							}
							return config;
						}).ToList();
				}
			}
			catch
			{
				// 吞掉所有异常，以防止该类因在 static 构造函数中发生异常而无法使用
			}

			return configedList;
		}

	}
} 
